#!/usr/bin/env python
# -*- python -*-
# vim: syntax=python
import time
import itertools
### basis for the revision string
base_rev_major = "0."
base_rev = base_rev_major
master_repo = 'git://github.com/springlobby/springlobby.git'
master_repo_rw = 'git@github.com:springlobby/springlobby.git'

WIN_STRIP="/opt/mingw32/usr/bin/i686-w64-mingw32-strip"
WIN_EXE_DIR="build-msw-default/src/"

c = BuildmasterConfig = {}
c['slaves'] = []
c['builders'] = []
c['schedulers'] = []
c['status'] = []

####### BUILDSLAVES

from buildbot.buildslave import BuildSlave

#import the pw vars from another file. this way we can keep config in git
import pw

#c['slaves'].append(BuildSlave("testbot", pw.my_pw, max_builds=1))
c['slaves'].append(BuildSlave("release-slave", pw.release_slave_pw, max_builds=1))
c['slaves'].append(BuildSlave("koshi-springlobby-slave", pw.koshi_springlobby_slave_pw, max_builds=1))
c['slaves'].append(BuildSlave("koshi-springlobby-slave2", pw.koshi_springlobby_slave2_pw, max_builds=1))
c['slaves'].append(BuildSlave("documentation", pw.documentation_pw, max_builds=1))
#c['slaves'].append(BuildSlave("macosx1", pw.mac_pw, max_builds=1))
c['slavePortnum'] = 9989

### CONFIGS

# maybe see: /opt/mingw32/usr/i686-w64-mingw32/share/cmake/mxe-conf.cmake
common_win_opts = [
		'-DCMAKE_BUILD_TYPE=RelWithDebInfo',
		'-DAUX_VERSION=msw',
		'-DCMAKE_INSTALL_PREFIX:PATH=.',
	]
no_opti  = ['-DCMAKE_CXX_FLAGS:STRING=-O0']
common_nix_opts = []
build_configs = dict() # name -> options_string
build_configs['msw-default']  =  common_win_opts + ['-DCMAKE_CXX_FLAGS:STRING=-DNDEBUG']
build_configs['msw-debug']  =  common_win_opts  + ['-DCMAKE_BUILD_TYPE=DEBUG', '-DCMAKE_CXX_FLAGS:STRING=-g3 -ggdb -O0']
build_configs['msw-no-optionals']  = common_win_opts + no_opti

build_configs['linux-default']  = no_opti + common_nix_opts
build_configs['linux-no-optionals']  = ['-DCMAKE_BUILD_TYPE=PROFILE'] + no_opti + common_nix_opts

common_mac_opts = "-DCMAKE_TOOLCHAIN_FILE:STRING=../cmake/Toolchain-osx.cmake"
#build_configs['osx-default'] = ' ' + common_mac_opts
#builds with extra env file sourced
build_env_configs = dict() # name -> options_string
#disabled cause compiler segfaults
#build_env_configs['gcc-svn-linux-default']  = "-DCMAKE_CXX_COMPILER=/opt/gcc-svn/bin/g++ -DCMAKE_C_COMPILER=/opt/gcc-svn/bin/gcc -DCMAKE_LINKER=/opt/gcc-svn/bin/ld" + no_opti + common_nix_opts
build_env_configs['wx2.9-linux-default']  = no_opti + common_nix_opts
build_env_configs['clang-linux-default']  = [
			'-DCMAKE_CXX_COMPILER=/opt/llvm/bin/clang++',
			'-DCMAKE_C_COMPILER=/opt/llvm/bin/clang',
			'-DCMAKE_LINKER=/opt/llvm/bin/llvm-ld' ] + no_opti + common_nix_opts
env_builds = [ ]
#env_builds.append( ( "/opt/gcc-svn.PATH", "gcc-svn-linux-default" ) )
env_builds.append( ( "/opt/wx2.9.PATH", "wx2.9-linux-default" ) )
env_builds.append( ( "/opt/llvm.PATH", "clang-linux-default" ) )

####### CHANGESOURCES

def changeIsImportant(change):
	for name in change.files:
		if (name.endswith('.cpp') or name.endswith('.c') or
		    name.endswith('.hpp') or name.endswith('.h') or
		    name.endswith('.cmake') or name.endswith('.hh') or
		    name.endswith('.inl') or name.endswith('CMakeLists.txt')):
			return True
	return False

from buildbot.changes.pb import PBChangeSource
c['change_source'] = []
c['change_source'].append(PBChangeSource())

####### SCHEDULERS

from buildbot.schedulers.filter import ChangeFilter
from buildbot.schedulers.basic import AnyBranchScheduler
from buildbot.schedulers.forcesched import ForceScheduler
from buildbot.schedulers import timed

c['schedulers'].append(AnyBranchScheduler(
      name="develop_Scheduler",
      #change_filter=ChangeFilter(category_re = '.*develop.*'),
      treeStableTimer=60,
      fileIsImportant=changeIsImportant,
      builderNames=["develop-win"]))

c['schedulers'].append(ForceScheduler(
	name='force_sched',
	builderNames=["docs", "release", "mxe", 'develop', 'develop-win_debug', 'develop-win'] ))

####### BUILDERS

from buildbot.process.factory import BuildFactory
from buildbot.steps.source import Git
from buildbot.steps.shell import Compile, ShellCommand, WithProperties

class SyncSubmodules(ShellCommand) :
	name = "syncsubmodules"
	description = 'syncing .gitmodules with .git/config'
	descriptionDone = 'synced submodule paths'
	command = ['git', 'submodule', 'sync']

class InitSubmodules(ShellCommand) :
	name = "submodules"
	description = 'updating submodules'
	descriptionDone = 'updated submodules'
	command = ['git', 'submodule', 'update', '--init']

class BuildDocs(ShellCommand) :
	name = "execute doxygen"
	description = ["generating doxygen documentation"]
	descriptionDone = ["docs generated"]
	command = ["/bin/sh","./tools/update-docs.sh"]

class UploadTranslations(ShellCommand) :
	name = "UploadTranslations"
	description = ["Upload translations"]
	descriptionDone = ["translations uploaded"]
	command = ["sl-update-translations","po/springlobby.pot"]

class WindowsBinary(ShellCommand) :
	name = "windows binary"
	description = ["making windows binary"]
	descriptionDone = ["windows binary"]
	command = ["zip", "-jr", WithProperties("build-msw-default/springlobby-"+base_rev+"%(buildnumber)s-win32.zip"), "build-msw-default/wininst"]

class WindowsLocales(ShellCommand) :
	name = "windows locale"
	description = ["adding locales to win zip"]
	descriptionDone = ["windows locale added"]
	command = ["/bin/sh", "./tools/buildbot-add-locale-win-zip.sh", WithProperties("springlobby-"+base_rev+"%(buildnumber)s-win32.zip")]

class ReleaseWin(ShellCommand) :
	name = "release windows binary"
	description = ["releasing windows binary"]
	descriptionDone = ["windows binary release"]
	command = ["/usr/bin/install","-t", "/data/www/springlobby.info/windows/", WithProperties("build-msw-default/springlobby-"+base_rev+"%(buildnumber)s-win32.zip")]

class ReleaseWinInstaller(ShellCommand) :
	name = "release windows installer"
	description = ["releasing windows binary"]
	descriptionDone = ["windows binary release"]
	command = ["/usr/bin/install","-t", "/data/www/springlobby.info/windows/", WithProperties("build-msw-default/SpringLobby-"+base_rev+"%(buildnumber)s.exe")]

class ReleaseTarball(ShellCommand) :
	name = "release tarball"
	description = ["releasing tarball"]
	descriptionDone = ["tarball release"]
	command = ["/usr/bin/install", "-t", "/data/www/springlobby.info/tarballs/", WithProperties("build-msw-default/springlobby-"+base_rev+"%(buildnumber)s.tar.gz"), WithProperties("build-msw-default/springlobby-"+base_rev+"%(buildnumber)s.tar.bz2")]

class GitTag(ShellCommand) :
	name = "git tag"
	description = "git tagging"
	descriptionDone = "git tag"
	command = ["git","tag", "-a", "-m", WithProperties(base_rev+"%(buildnumber)s"),  WithProperties(base_rev+"%(buildnumber)s")]

class LatestZip(ShellCommand) :
	name = "latest zip"
	description = "latest zip"
	descriptionDone = "latest zip"
	command = ["tools/latest.sh", WithProperties(base_rev+"%(buildnumber)s")]

class PublishVersion(ShellCommand) :
	name = "publish version number"
	description = "publishing version"
	descriptionDone = "published version"
	command = ["/bin/bash", "-c", WithProperties("echo "+base_rev+"%(buildnumber)s > /data/www/springlobby.info/version/current.txt")]

# TODO osc needs user/pass for opensuse build service
# but of course we can't set them here, so you have to do it manually the first time
class UpdateRPM(ShellCommand) :
	name = "update rpm"
	description = "updating rpm"
	descriptionDone = "updated rpm"
	command = ["/bin/bash", "./tools/update-rpm.sh", WithProperties(base_rev+"%(buildnumber)s")]

class UpdateEbuild(ShellCommand) :
	name = "update ebuild"
	description = "updating ebuild"
	descriptionDone = "updated ebuild"
	command = ["/bin/bash", "./tools/update-ebuilds.sh", WithProperties(base_rev+"%(buildnumber)s")]

class UpdateTranslations(ShellCommand) :
	name = "update translations"
	description = "updating translations"
	descriptionDone = "updated translations"
	command = ["/bin/bash", "./tools/update-translations-buildbot.sh"]

class UploadBundle(ShellCommand) :
	name = "upload bundle"
	def __init__(self, configname='osx-default',who='buildbot',**kwargs):
		self.configname = configname
		self.who 		= who
		ShellCommand.__init__(self, **kwargs)

		#mandatory for later (automatic) re-creation of step object
		self.addFactoryArguments(configname = configname)
		self.addFactoryArguments(who= who)
		self.description = "uploading bundle"
		self.descriptionDone = "uploaded bundle"
		self.command = ["/bin/bash", "./tools/upload-macosx-build.sh", "build-%s"%configname, self.who, WithProperties("%s","branch")]

class UploadTempBuild(ShellCommand) :
	name = "upload exe"
	def __init__(self, configname='linux-default',who='koshi',**kwargs):
		self.configname = configname
		self.who 		= who
		ShellCommand.__init__(self, **kwargs)

		#mandatory for later (automatic) re-creation of step object
		self.addFactoryArguments(configname = configname)
		self.addFactoryArguments(who= who)
		self.description = "uploading exe"
		self.descriptionDone = "uploaded exe"
		self.command = ["/bin/bash", "./tools/upload-temp-win-build.sh", "build-%s"%configname, self.who, WithProperties("%s","branch")]

class UploadTempDebugBuild(ShellCommand) :
	name = "upload exe"
	def __init__(self, configname='linux-default', who='koshi', **kwargs):
		self.configname = configname
		self.who 		= who
		ShellCommand.__init__(self, **kwargs)

		#mandatory for later (automatic) re-creation of step object
		self.addFactoryArguments(configname = configname)
		self.addFactoryArguments(who= who)
		self.description = "uploading exe"
		self.descriptionDone = "uploaded exe"
		self.command = ["/bin/bash", "./tools/upload-temp-win-build_debug.sh", "build-%s"%configname, self.who, WithProperties("%s","branch")]

# common build config steps ---------------------------------------------@
class buildConfig(Compile):
	name = 'buildConfig'
	def __init__(self, configname='linux-default',jobs=1,release=False,**kwargs):
		self.configname = configname
		self.release = release
		Compile.__init__(self, **kwargs)
		self.haltOnFailure = True

		#mandatory for later (automatic) re-creation of step object
		self.addFactoryArguments(configname = configname)
		self.addFactoryArguments(jobs = jobs)
		self.addFactoryArguments(release= release)

		#self.name 			= self.configname + " build"
		self.description 	= ["building " + self.configname + " config"]
		self.descriptionDone	= ["built " + self.configname + " config"]
		if self.release:
			self.command		= ['make' ,'BUILDBOT_RELEASE=1','-k', '-j%d'%jobs, '-C', 'build-%s'%(self.configname)]
		else:
			self.command		= ['make' ,'-k', '-j%d'%jobs, '-C', 'build-%s'%(self.configname)]

class buildEnvConfig(Compile):
	name = 'buildConfig'
	def __init__(self, env_file,configname,jobs=1,release=False,**kwargs):
		self.configname = configname
		self.release = release
		self.env_file = env_file
		Compile.__init__(self, **kwargs)
		self.haltOnFailure = True

		#mandatory for later (automatic) re-creation of step object
		self.addFactoryArguments(configname = configname)
		self.addFactoryArguments(jobs = jobs)
		self.addFactoryArguments(release= release)
		self.addFactoryArguments(env_file= env_file)
		if 'gcc-svn' in configname:
			self.addFactoryArguments(flunkOnFailure=False)
			self.addFactoryArguments(haltOnFailure=False)

		#self.name 			= self.configname + " build"
		self.description 	= ["building " + self.configname + " config"]
		self.descriptionDone	= ["built " + self.configname + " config"]
		if self.release:
			self.command		= ' '.join(['source', self.env_file, '&&', 'make' ,'BUILDBOT_RELEASE=1','-k', '-j%d'%jobs, '-C', 'build-%s'%(self.configname)])
		else:
			self.command		= ' '.join(['source', self.env_file, '&&', 'make' ,'-k', '-j%d'%jobs, '-C', 'build-%s'%(self.configname)])

# TODO fail and stop the build if this fails
class CreateBuildDir(ShellCommand) :
	name = "build dir create"

	def __init__(self, configname='linux-default',**kwargs):
		self.configname = configname
		ShellCommand.__init__(self, **kwargs)

		#mandatory for later (automatic) re-creation of step object
		self.addFactoryArguments(configname = configname)
		self.haltOnFailure = True

		self.description = ["creating dir %s"%(self.configname)]
		self.descriptionDone = ["%s dir created"%(self.configname)]
		self.command = ["/bin/bash", "./buildbot/create-build-dirs.sh", self.configname, WithProperties(base_rev+"%(buildnumber)s") , ] + build_configs[self.configname]

class CreateEnvBuildDir(ShellCommand) :
	name = "build dir create"

	def __init__(self, env_file,configname='linux-default',**kwargs):
		self.configname = configname
		self.env_file = env_file
		ShellCommand.__init__(self,**kwargs)

		#mandatory for later (automatic) re-creation of step object
		self.addFactoryArguments(configname = configname)
		self.addFactoryArguments(env_file= env_file)
		self.haltOnFailure = True

		self.description = ["creating dir %s"%(self.configname)]
		self.descriptionDone = ["%s dir created"%(self.configname)]
		self.command = ["source", self.env_file, "&&", "/bin/bash", "./buildbot/create-build-dirs.sh", self.configname, str(time.time()), build_env_configs[self.configname]]

class CreateWinInstaller(ShellCommand):
	name = "create win installer"

	def __init__(self, configname='msw-default',**kwargs):
		self.configname = configname
		ShellCommand.__init__(self, **kwargs)

		#mandatory for later (automatic) re-creation of step object
		self.addFactoryArguments(configname = configname)
		self.haltOnFailure = True

		self.description = ["creating dir %s"%(self.configname)]
		self.descriptionDone = ["%s dir created"%(self.configname)]
		self.command = ["installer/make_installer.sh", WithProperties("build-msw-default/springlobby-"+base_rev+"%(buildnumber)s-win32.zip") ]


# common build config steps ---------------------------------------------@
JOBS='-j2'

class ReleaseFactory(BuildFactory):
	def __init__(self):
		BuildFactory.__init__(self)
		self.addStep(Git(repourl=master_repo))
		self.addStep( SyncSubmodules() )
		self.addStep( InitSubmodules() )
		self.addStep(CreateBuildDir('msw-default'))
		self.addStep(GitTag())
		self.addStep(ShellCommand(command=["git","push", "--tags", master_repo_rw]))
		self.addStep(ShellCommand(command=["git","push", master_repo_rw, "master"]))
		self.addStep( buildConfig(configname='msw-default',jobs=2,release=True) )
		self.addStep(Compile(command=["make", 'BUILDBOT_RELEASE=1',JOBS, "-C", "build-msw-default", "pack", "dist"]))
		self.addStep(ReleaseTarball())
		self.addStep(CreateWinInstaller())
		self.addStep(ReleaseWin())
		self.addStep(ReleaseWinInstaller())
		self.addStep(PublishVersion())
		#self.addStep(UpdateRPM())
		#self.addStep(UpdateEbuild())
		#self.addStep(UploadTranslations())
		self.addStep(LatestZip())

class FullBuildFactory(BuildFactory):
	def __init__(self,dude):
		BuildFactory.__init__(self)
		self.addStep(Git(repourl=master_repo))
		self.addStep( SyncSubmodules() )
		self.addStep( InitSubmodules() )
		for name in set(build_configs.keys()) - set(['osx-default']):
			self.addStep( CreateBuildDir(name) )
			self.addStep( buildConfig(configname=name,jobs=2) )
		for env in env_builds:
			self.addStep( CreateEnvBuildDir(env[0],env[1]) )
			self.addStep( buildEnvConfig(env[0],env[1],jobs=2) )
		self.addStep(Compile(command=["make", "-j2","-C", "build-linux-default", "distcheck"]))
class TestBuildFactory(BuildFactory):
	def __init__(self,dude):
		BuildFactory.__init__(self)
		self.addStep(Git(repourl=master_repo))
		self.addStep( SyncSubmodules() )
		self.addStep( InitSubmodules() )
		for env in env_builds:
			self.addStep( CreateEnvBuildDir(env[0],env[1]) )
			self.addStep( buildEnvConfig(env[0],env[1],jobs=2) )

class WinTempBuildFactory(BuildFactory):
	def __init__(self, who):
		BuildFactory.__init__(self)
		self.addStep(Git(repourl=master_repo))
		self.addStep( SyncSubmodules() )
		self.addStep( InitSubmodules() )
		self.addStep( CreateBuildDir('msw-default') )
		self.addStep( buildConfig(configname='msw-default',jobs=2) )
		self.addStep(UploadTempBuild(configname='msw-default',who=who  ))

class WinTempDebugBuildFactory(BuildFactory):
	def __init__(self, who):
		BuildFactory.__init__(self)
		self.addStep(Git(repourl=master_repo))
		self.addStep( SyncSubmodules() )
		self.addStep( InitSubmodules() )
		self.addStep( CreateBuildDir('msw-debug') )
		self.addStep( buildConfig(configname='msw-debug',jobs=2) )
		self.addStep(UploadTempDebugBuild(configname='msw-debug',who=who  ))

class OSXBuildFactory(BuildFactory):
	def __init__(self, who):
		BuildFactory.__init__(self)
		self.addStep(Git(repourl=master_repo))
		self.addStep( SyncSubmodules() )
		self.addStep( InitSubmodules() )
		self.addStep( CreateBuildDir('osx-default') )
		self.addStep( buildConfig(configname='osx-default',jobs=1) )
		self.addStep(UploadBundle(configname='osx-default',who=who  ))

class CreateMXEBuildDir(ShellCommand):
	name = "createmxebuilddir"
	description = 'creating build dir'
	descriptionDone = 'created build dir'
	command = ['tools/mxe_create_builddir.sh']

class CompileMXEBuildDir(ShellCommand):
	name = "createmxebuilddir"
	description = 'compiling mxe'
	descriptionDone = 'compiled mxe'
	command = ['tools/mxe_compile.sh']

class MXEFactory(BuildFactory):
	def __init__(self):
    		BuildFactory.__init__(self)
		self.addStep(Git(repourl=master_repo))
		self.addStep(CreateMXEBuildDir())
		self.addStep(CompileMXEBuildDir())


win32buildenv = {
		'CMAKE': '/home/buildbot/slaves/springlobby/mxe/build/mxe/usr/bin/i686-w64-mingw32.static-cmake',
		'CMAKE_PARAMS': '-DCMAKE_STRIP:PATH=/home/buildbot/slaves/springlobby/mxe/build/mxe/usr/bin/i686-w64-mingw32.static-strip'
	}

c['builders'].append({'name': 'develop',
		'slavenames': ["release-slave"],
		'builddir': "develop",
		'factory': FullBuildFactory("develop")
	})
c['builders'].append({'name': 'develop-win',
		'slavenames': ["release-slave"],
		'builddir': 'develop-win',
		'factory': WinTempBuildFactory("develop"),
		'env': win32buildenv
	})

c['builders'].append({'name': 'develop-win_debug',
		'slavenames': ["release-slave"],
		'builddir': 'develop-win_debug',
		'factory': WinTempDebugBuildFactory("develop"),
		'env': win32buildenv
	})

c['builders'].append({'name': "release",
		'slavenames': ["release-slave"],
		'builddir': "release",
		'factory': ReleaseFactory(),
		'env': win32buildenv
	})

f2 = BuildFactory()
f2.addStep(Git(repourl=master_repo))
f2.addStep(BuildDocs())

c['builders'].append({'name': "docs",
		'slavename': "documentation",
		'builddir': "docs",
		'factory': f2,
	})

c['builders'].append({'name': 'mxe',
		'slavenames': ["release-slave"],
		'builddir': 'mxe',
		'factory': MXEFactory()
	})

####### STATUS TARGETS
from buildbot.status.html import WebStatus
from buildbot.status.web.authz import Authz
from buildbot.status.web.auth import HTPasswdAuth
auth = (HTPasswdAuth('/home/buildbot/.htpasswd'))
authz = Authz(auth=auth,
	forceBuild='auth', # only authenticated users
	pingBuilder=True, # but anyone can do this
)
from buildbot.status.html import WebStatus

c['status'].append(WebStatus(
		http_port=8010,
		authz=authz,
		#change_hook_dialects={ 'github' : True }
	))

from buildbot.changes.gitpoller import GitPoller
c['change_source'].append(GitPoller(master_repo,
                               branches=['master']))

#from buildbot.status import words
#c['status'].append(words.IRC(host="orwell.freenode.net", nick="springlobby",
#                             channels=["#springlobby"]))

from buildbot.status import client
c['status'].append(client.PBListener(9988))

from buildbot.status.mail import MailNotifier
#mn = MailNotifier(fromaddr="koshi@springlobby.info",builders=['release'],
#                       sendToInterestedUsers=False,
#                       extraRecipients=['releases@www.springlobby.info'])
bnames = ['koshi-win_dbg','dummy','tc','koshi','release','docs','BrainDamage','bd-win',
	'tobi','olemars','koshi-win_dbg','koshi-full','koshi-win','olemars-win','tc-win']

from buildbot.status import words
irc = words.IRC("irc.springrts.com", "SL_Release_Bot",
        channels=["springlobby"],
        password=pw.releasebot,
        notify_events={
		'successToFailure': 1,
		'warningsToFailure': 1,
		'failureToSuccess': 1,
		'failureToWarnings': 1,
		'success': 1,
        })
c['status'].append(irc)



####### DEBUGGING OPTIONS

#c['debugPassword'] = "debugpassword"
#c['manhole'] = buildbot.manhole.PasswordManhole("tcp:9999:interface=127.0.0.1", "admin", "password")

####### PROJECT IDENTITY

c['projectName'] = "SpringLobby"
c['projectURL'] = "http://springlobby.info/"
c['buildbotURL'] = "http://buildbot.springlobby.info/"
